package com.ts.api.module.api.controller;

import com.gitee.apanlh.annotation.viewresolver.RequestParamVo;
import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.reflection.CopyUtils;
import com.gitee.apanlh.util.thread.Sleep;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
import com.gitee.apanlh.web.model.vo.RequestVO;
import com.gitee.apanlh.web.model.vo.ResultVO;
import com.ts.api.common.constant.BizEnum;
import com.ts.api.common.constant.api.ApiServiceCodeEnum;
import com.ts.api.common.constant.common.CommonConstant;
import com.ts.api.common.constant.productflow.ProductApplyFlowStatusEnum;
import com.ts.api.common.context.ApiContext;
import com.ts.api.common.context.ChannelContext;
import com.ts.api.exp.BizException;
import com.ts.api.module.api.entity.bo.ApiRequiredFlowBO;
import com.ts.api.module.api.entity.bo.H5CallbackExistBO;
import com.ts.api.module.api.entity.bo.H5CallbackOrgInfoBO;
import com.ts.api.module.api.entity.po.ApiKeyPO;
import com.ts.api.module.api.entity.vo.RespH5AcceptCallbackVO;
import com.ts.api.module.api.entity.vo.RespH5CallbackExtendVO;
import com.ts.api.module.api.entity.vo.RespH5OrgCallbackVO;
import com.ts.api.module.api.service.ApiBizRequiredService;
import com.ts.api.module.api.service.ApiH5CallBackService;
import com.ts.api.module.channel.entity.po.ChannelMarketPO;
import com.ts.api.module.channel.entity.po.ChannelUserRegisterPO;
import com.ts.api.module.channel.entity.qo.ChannelUserQO;
import com.ts.api.module.channel.entity.vo.RespChannelMarketVO;
import com.ts.api.module.channel.service.ChannelMarketService;
import com.ts.api.module.channel.service.ChannelUserService;
import com.ts.api.module.product.entity.po.ProductApplyFlowPO;
import com.ts.api.module.product.entity.qo.ProductApplyFlowQO;
import com.ts.api.module.product.service.ProductApplyFlowService;
import com.ts.api.module.protocol.entity.po.ProtocolPO;
import com.ts.api.module.protocol.service.ProtocolService;
import com.ts.api.module.visitor.service.ChannelVisitorService;
import com.ts.cache.apikey.ApiKeyCacheService;
import com.ts.cache.apikey.entity.ApiKeyCache;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 *  h5回调页面相关
 *
 *  @author Pan
 */
@RestController
@RequestMapping("/h5/callback")
public class ApiH5CallbackController {

    @Autowired
    private ApiH5CallBackService apiH5CallBackService;
    @Autowired
    private ProductApplyFlowService productApplyFlowService;
    @Autowired
    private ProtocolService protocolService;
    @Autowired
    private ChannelMarketService channelMarketService;
    @Autowired
    private ChannelVisitorService channelVisitorService;
    @Autowired
    private ChannelUserService channelUserService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ApiKeyCacheService apiKeyCacheService;
    @Autowired
    private ApiBizRequiredService apiBizRequiredService;

    /**
     *   获取回调机构信息包含协议内容
     *
     *   @param   requestVO
     *   @return  ResultVO
     */
    @GetMapping("/getOrg")
    public ResultVO<Object> getOrg(@RequestParamVo RequestVO requestVO) {
        String phoneMd5 = requestVO.get("applyId", String.class);
        String apiClientId = requestVO.get("code", String.class);
        Assert.isNotEmptyThrows(phoneMd5, new BizException(BizEnum.EMPTY));
        Assert.isNotEmptyThrows(apiClientId, new BizException(BizEnum.EMPTY));

        //  检测是否有此API渠道
        ApiKeyCache apiKeyCache = apiKeyCacheService.get(apiClientId);
        Assert.isNotNullThrows(apiKeyCache, new BizException(BizEnum.API_KEY_CACHE_NULL));

        //  获取机构信息
        H5CallbackOrgInfoBO orgInfo = null;
        RLock lock = redissonClient.getLock("h5_flow_lock:" + phoneMd5 + ":" + apiClientId);
        lock.lock(10, TimeUnit.SECONDS);
        try {
            H5CallbackExistBO h5CallbackExistBO = H5CallbackExistBO.create(phoneMd5, apiClientId);

            //  获取用户进入回调流程数据
            List<Integer> statusList = CollUtils.newArrayList(newList -> {
                newList.add(ProductApplyFlowStatusEnum.CALLBACK_USER_INTO.getType());
                newList.add(ProductApplyFlowStatusEnum.USER_ACCEPT.getType());
            });

            //  获取流程数据
            ProductApplyFlowQO productApplyFlowQO = new ProductApplyFlowQO();
            productApplyFlowQO.setPhoneMd5(phoneMd5);
            productApplyFlowQO.setApiClientId(apiClientId);
            productApplyFlowQO.setStatusList(statusList);
            List<ProductApplyFlowPO> list = productApplyFlowService.list(productApplyFlowQO);

            //  获取用户进入授权页面页面
            ProductApplyFlowPO userInto = CollUtils.findOne(list, t -> Eq.object(ProductApplyFlowStatusEnum.CALLBACK_USER_INTO.getType(), t.getStatus()));
            if (ValidParam.isNotNull(userInto)) {
                h5CallbackExistBO.setHasCallUserInto(true);
            }

            //  获取用户授权流程数据
            ProductApplyFlowPO userAccept = CollUtils.findOne(list, t -> Eq.object(ProductApplyFlowStatusEnum.USER_ACCEPT.getType(), t.getStatus()));
            if (ValidParam.isNotNull(userAccept)) {
                h5CallbackExistBO.setHasCallUserAccept(true);
            }

            //  获取机构信息
            orgInfo = apiH5CallBackService.getOrgInfo(h5CallbackExistBO);
        } finally {
            lock.unlock();
        }

        //  返回实体
        RespH5OrgCallbackVO respOrg = new RespH5OrgCallbackVO();
        respOrg.setName(orgInfo.getName());
        respOrg.setLogoPath(orgInfo.getLogoPath());
        respOrg.setApplyStatus(orgInfo.getApplyStatus());

        //  如果已经授权则直接返回
        String applyStatus = orgInfo.getApplyStatus();
        if ("1".equals(applyStatus)) {
            return ResultVO.suc(respOrg);
        }

        //  相关协议
        StringBuilder protocol = new StringBuilder();
        //  个人信息授权协议
//        ProtocolTemplateBO protocolUserSharingBO = new ProtocolTemplateBO();
//        protocolUserSharingBO.setUserName(orgInfo.getUserName());
//        protocolUserSharingBO.setUserAccount(orgInfo.getPhone());
//        protocolUserSharingBO.setOrgName(orgInfo.getName());
//        protocolUserSharingBO.setUserPhone(orgInfo.getPhone());
//        protocolUserSharingBO.setCurrentDate(DateUtils.currentTime(DateTimeFormat.DATE_FULL));
//        String protocolText = protocolDynamicService.replaceByOrder("5d66cd548f7048da935d0894d5c17b06", protocolUserSharingBO);
//        protocol.append(protocolText);
        //  隐私保护政策
        ProtocolPO protocolText2 = protocolService.getProtocolText(CommonConstant.H5_CALL_AUTH_USER_PROTOCOL_CODE);
        protocol.append(protocolText2.getText());

        //  拓展数据---固定的值
        respOrg.setTextMax("200000");
        respOrg.setTextMatch("50000");
        respOrg.setTextAnnualRate("7.20%");
        respOrg.setTextTerm("36");

        respOrg.setProtocolName("个人信息共享授权协议、隐私保护政策");
        respOrg.setProtocolContent(protocol.toString());

        //  自动授权
        respOrg.setEnableAccept(apiKeyCache.getApiEnableAccept());
        return ResultVO.suc(respOrg);
    }

    /**
     *  用户同意授权
     *
     *  @param  requestVO
     *  @return ResultVO
     */
    @PostMapping("/accept")
    public ResultVO<Object> accept(@RequestParamVo RequestVO requestVO) {
        ApiContext.SERVICE_CODE.set(ApiServiceCodeEnum.A0002.getCode());

        //  phoneMd5
        String applyId = requestVO.get("applyId", String.class);
        //  客户端id
        String apiClientId = requestVO.get("code", String.class);
        Assert.isNotEmptyThrows(applyId, new BizException(BizEnum.EMPTY));
        Assert.isNotEmptyThrows(apiClientId, new BizException(BizEnum.EMPTY));

        //  检测是否有此API渠道
        ApiKeyCache apiKeyCache = apiKeyCacheService.get(apiClientId);
        Assert.isNotNullThrows(apiKeyCache, new BizException(BizEnum.API_KEY_CACHE_NULL));
        ApiKeyPO apiKeyPO = CopyUtils.copy(apiKeyCache, ApiKeyPO.class);

        //  获取所需流程数据
        List<Integer> statusList = CollUtils.newArrayList(newList -> {
            newList.add(ProductApplyFlowStatusEnum.CALLBACK_USER_INTO.getType());
            newList.add(ProductApplyFlowStatusEnum.USER_ACCEPT.getType());
        });
        ProductApplyFlowQO productApplyFlowQO = new ProductApplyFlowQO();
        productApplyFlowQO.setPhoneMd5(applyId);
        productApplyFlowQO.setApiClientId(apiClientId);
        productApplyFlowQO.setStatusList(statusList);
        List<ProductApplyFlowPO> list = productApplyFlowService.list(productApplyFlowQO);

        //  没进入授权页面抛出异常
        ProductApplyFlowPO userInto = CollUtils.findOne(list, t -> Eq.object(ProductApplyFlowStatusEnum.CALLBACK_USER_INTO.getType(), t.getStatus()));
        Assert.isNotNullThrows(userInto, new BizException(BizEnum.API_CALL_FLOW_USER_EMPTY));

        //  用户授权同意
        ProductApplyFlowPO userAccept = CollUtils.findOne(list, t -> Eq.object(ProductApplyFlowStatusEnum.USER_ACCEPT.getType(), t.getStatus()));
        if (ValidParam.isNull(userAccept)) {
            //  查询是否渠道存在该流程
            ApiRequiredFlowBO apiRequiredFlowBO = ApiRequiredFlowBO.createByApiClientId(applyId, apiKeyPO.getApiClientId());
            ProductApplyFlowPO productApplyFlowPO = apiBizRequiredService.requiredSucDataExchangeFlow(apiRequiredFlowBO);
            Assert.isNotNullThrows(productApplyFlowPO, new BizException(BizEnum.API_CALL_FLOW_EMPTY));

            //  拉取上游进件申请
            if (ValidParam.isNotEmpty(apiKeyPO.getApiPullApplyNotifyUrl())) {
                apiH5CallBackService.pullApply(apiKeyPO, productApplyFlowPO);
                Thread.startVirtualThread(() -> {
                    int retry = 0;
                    //  最大尝试次数
                    int maxRetry = 240;
                    while (retry < maxRetry) {
                        String phoneMd5 = productApplyFlowPO.getPhoneMd5();
                        //  查询用户信息
                        ChannelUserQO userQO = new ChannelUserQO();
                        userQO.setPhoneMd5(phoneMd5);
                        ChannelUserRegisterPO userRegisterPO = channelUserService.get(userQO);
                        //  进件
                        if (ValidParam.isNotNull(userRegisterPO)) {
                            apiH5CallBackService.apply(apiKeyPO, productApplyFlowPO);
                            return ;
                        }
                        retry ++;
                        Sleep.seconds(1);
                    }
                });
            } else {
                //  撞库进件模式
                apiH5CallBackService.apply(apiKeyPO, productApplyFlowPO);
            }
        }

        // 获取渠道是否要返回渠道url而非返回最终页
        RespH5AcceptCallbackVO result = new RespH5AcceptCallbackVO();
        result.setApiRedirectUrl(apiKeyCache.getApiRedirectUrl());
        return ResultVO.suc(result);
    }

    /**
     *  获取回调拓展业务
     *
     *  @param  requestVO
     *  @return ResultVO
     */
    @PostMapping("/getExtend")
    public ResultVO<Object> getExtend(@RequestParamVo RequestVO requestVO) {
        String applyId = requestVO.get("applyId", String.class);
        String apiClientId = requestVO.get("code", String.class);
        Assert.isNotEmptyThrows(applyId, new BizException(BizEnum.EMPTY));
        Assert.isNotEmptyThrows(apiClientId, new BizException(BizEnum.EMPTY));

        //  检测是否有此API渠道
        ApiKeyCache apiKeyCache = apiKeyCacheService.get(apiClientId);
        Assert.isNotNullThrows(apiKeyCache, new BizException(BizEnum.API_KEY_CACHE_NULL));

        RespH5CallbackExtendVO respH5CallbackExtendVO = new RespH5CallbackExtendVO();
        respH5CallbackExtendVO.setRedirectUrl(apiKeyCache.getApiRedirectUrl());
        return ResultVO.suc(respH5CallbackExtendVO);
    }

    /**
     *  贷超-用户点击贷超访问
     *
     *  @param  requestVO
     *  @return ResultVO
     */
    @PostMapping("/visitor/market")
    public ResultVO<Object> visitorMarket(@RequestParamVo RequestVO requestVO) {
        String applyId = requestVO.get("applyId", String.class);
        String apiClientId = requestVO.get("code", String.class);
        Integer market = requestVO.get("marketId", Integer.class);
        //  如果没有返回产品id代表没有贷超配置
        if (market == null) {
            return ResultVO.suc();
        }
        long marketId = ClassConvertUtils.toLong(market);

        //  检测是否有此API渠道
        ApiKeyCache apiKeyCache = apiKeyCacheService.get(apiClientId);
        Assert.isNotNullThrows(apiKeyCache, new BizException(BizEnum.API_KEY_CACHE_NULL));
        Assert.isNotNullThrows(applyId, new BizException(BizEnum.EMPTY));
        Assert.isNotNullThrows(apiClientId, new BizException(BizEnum.EMPTY));
        Assert.isNotNullThrows(marketId, new BizException(BizEnum.EMPTY));

        //  不存在用户授权则不能点击
        ProductApplyFlowQO countFlowQO = ProductApplyFlowQO.createCustomFlowByPhoneMd5(applyId, ProductApplyFlowStatusEnum.USER_ACCEPT);
        Assert.isTrueThrows(productApplyFlowService.countStatus(countFlowQO) > 0, new BizException(BizEnum.API_CALL_FLOW_EMPTY));

        //  检测参数
        Long channelId = ChannelContext.getId();
        int count = channelMarketService.countByChannelIdAndMarketId(channelId, marketId);
        if (count == 0) {
            BizException.fastFail(BizEnum.MARKET_EMPTY);
        }

        //  获取流程信息
        ProductApplyFlowPO productApplyFlowPO = productApplyFlowService.get(ProductApplyFlowQO.createH5Callback(applyId, apiClientId));
        Assert.isNotNullThrows(productApplyFlowPO, new BizException(BizEnum.API_CALL_FLOW_EMPTY));

        //  获取用户信息
        ChannelUserQO userQO = ChannelUserQO.createPhoneMd5(productApplyFlowPO.getPhoneMd5());
        ChannelUserRegisterPO userRegisterPO = channelUserService.get(userQO);
        Assert.isNotNullThrows(userRegisterPO, new BizException(BizEnum.API_CALL_FLOW_USER_EMPTY));
        channelVisitorService.insertMarketVisitor(channelId, userRegisterPO.getId(), marketId);
        return ResultVO.suc();
    }

    /**
     *  贷超-回调贷超列表
     *
     *  @return ResultVO
     */
    @GetMapping("/market/list")
    public ResultVO<Object> listMarket() {
        List<ChannelMarketPO> list = channelMarketService.list(true);
        List<RespChannelMarketVO> listVO = CopyUtils.copyList(list, RespChannelMarketVO.class);
        return ResultVO.suc(listVO);
    }
}
